Skip to main content

灯泡 - CS50x 2023

并非完全损坏的灯泡

在课堂上,你可能已经注意到舞台前方似乎存在一个“bug”,其中一些灯泡似乎总是熄灭:

Week 2 讲座中灯泡条的屏幕截图

然而,每个灯泡序列都以二进制编码了一条消息,二进制是计算机说的语言。我们来编写一个程序来制作自己的秘密消息,说不定我们还能搬到舞台上去呢!

开始

打开 VS Code

首先点击终端窗口,然后输入 cd。你应该发现它的提示符类似于下面。

单击该终端窗口内部,然后执行

wget https://cdn.cs50.net/2022/fall/psets/2/bulbs.zip

然后按回车键,在你的 codespace 中下载名为 bulbs.zip 的 ZIP 文件。注意 wget 和 URL 之间有空格,别漏掉了,其他字符也一样!

现在执行

来创建一个名为 bulbs 的文件夹。现在可以删除 ZIP 文件了,输入

然后输入 y 并按回车键删除下载的 ZIP 文件。

现在输入

然后按回车键进入该目录。提示符应该类似下面这样。

如果没问题,输入

并看到一个名为 bulbs.c 的文件。code bulbs.c 会打开文件,你可以在里面写代码。如果没有,请回溯你的步骤,看看你是否可以确定你哪里出错了!

实现细节

要编写我们的程序,我们首先需要考虑进制

基础知识

最简单的进制是 1 进制,或一元; 要用 1 进制写一个数字 N,我们只需写 N 个连续的 1。 因此,1 进制中的数字 4 将写为 1111,数字 12 写为 111111111111。 可以把它想象成用手指计数或用木板上的标记来计算分数。

你可能会明白为什么现在很少使用 1 进制。(数字变得相当长!) 相反,一个常见的约定是 10 进制,或十进制。 在十进制中,每个数字都乘以 10 的某个幂,以便表示更大的数字。 例如, 是 的简写。

更改进制就像将上面更改为不同的数字一样简单。 例如,如果你用四进制写 123,那么它实际上表示 ,等于十进制的 。

然而,计算机使用 2 进制,或二进制。 在二进制中,写 123 将是一个错误,因为二进制数只能有 01。 但是,弄清楚二进制数代表的确切十进制数的过程是完全相同的。 例如,二进制数 10101 表示 ,它等于十进制数 。

编码消息

灯泡只能亮或灭。 换句话说,灯泡代表两种可能的状态; 灯泡要么亮,要么灭,就像二进制数要么是 1,要么是 0。 我们必须找到一种方法将文本编码为二进制数序列。

我们来写一个叫 bulbs 的程序,它可以把一段文字转换成灯泡的亮灭状态,展示给观众。我们将分两步完成:

  • 第一步包括将文本转换为十进制数。 比如我们要编码消息 HI!。 幸运的是,我们已经有了一个约定来做到这一点,ASCIIH 的十进制表示是 72I73!33
  • 下一步包括获取我们的十进制数(如 727333)并将它们转换为等效的二进制数,这些二进制数仅使用 0 和 1。 为了统一位数,我们用 8 位二进制数来表示每个十进制数。 720100100073010010013300100001

最后,我们将把这些二进制数解释为舞台上灯泡的指令; 0 是关闭,1 是打开。 bulbs.c 里有一个已经写好的 print_bulb 函数,输入 01 就能输出代表灯泡的 emoji。

这是一个已完成程序可能如何工作的示例。 和 Sanders 舞台不一样,为了更清楚,我们每行打印一个字节。

检查结果时,亮的灯泡 (🟡) 代表 1,灭的灯泡 (⚫) 代表 0。 那么 HI! 变成了

01001000
01001001
00100001

这正是我们所期望的。

另一个例子:

请注意,所有字符都包含在灯泡指令中,包括非字母字符,如空格 (00100000)。

规范

设计并实现一个程序 bulbs,将文本转换成 CS50 舞台灯条的控制指令,具体要求如下:

  • 请在名为 bulbs.c 的文件中实现你的程序。
  • 你的程序首先需要使用 get_string 函数提示用户输入信息。
  • 接下来,你的程序需要将输入的字符串 (string) 转换为一系列8位二进制数,每个字符对应一个二进制数。
  • 你可以使用提供的 print_bulb 函数打印一系列 01,这些数字会以黄色和黑色 emoji 的形式显示,分别代表灯泡的亮和灭。
  • 每个包含 8 个符号的“字节”在输出时应单独占一行;并且在最后一个“字节”后也需要添加一个换行符 (\n)。

十进制转二进制的提示

我们以数字 4 为例进行说明。 你将如何将 4 转换为二进制? 首先考虑最右边的位,如果该位为 on,则会将 1 添加到我们所表示的数字。 你需要打开这个位吗? 将 4 除以 2 找出答案:

2 可以整除 4,这告诉我们没有余数 1 需要担心。 因此,我们可以安全地将最右边的位关闭:

那么,现在,前面的位,也就是我们发现的这个位左边的那个位呢? 为了检查,让我们按照类似的过程,但从我们离开的地方继续。 在上一步中,我们将 4 除以 2 得到 2。 现在,2 是否能整除 2 呢? 答案是可以,因此无需考虑余数 2。

让我们继续进一步。 将 2 除以 2 后,我们剩下 1。 将 1 除以 2 留下余数 1。 这意味着我们需要打开这个位:

现在我们已经将我们的数字除到 0,我们不再需要任何位来表示它。 注意,我们发现这些代表数字 4 的二进制位顺序,与最终需要打印的顺序正好相反:我们可能需要一个结构来让我们存储这些位,以便我们以后可以向前打印它们。 当然,在你的实际代码中,你将使用 8 位的 char,因此你需要预先添加任何需要的 0。

在计算余数时,取模运算符 (%) 会很有用! 例如,4 % 2 返回 0,这意味着 2 除以 4 的余数为 0。

如何测试你的代码

你的程序应该能像上述示例一样运行。 你可以使用 check50 检查你的代码,check50 是 CS50 在你提交时用来测试你的代码的程序,方法是在 $ 提示符下输入以下内容。 但务必自己也测试一下!

check50 cs50/problems/2023/x/bulbs

要评估你的程序代码风格,请在 $ 提示符下输入以下内容。

如何提交

在你的终端中,执行以下命令来提交你的工作。

submit50 cs50/problems/2023/x/bulbs